package com.black.cat.jvm.rtda.heap.constantpool;

import com.black.cat.jvm.classfile.constantpool.impl.ConstantInfoFieldref;
import com.black.cat.jvm.rtda.heap.methodarea.JClass;
import com.black.cat.jvm.rtda.heap.methodarea.JField;

/**
 * @Author blackcat
 * @create 2021/8/14 13:32
 * @version: 1.0
 * @description:
 */
public class FieldRef extends SymRef {

    private JField field;
    public String name;
    public String descriptor;

    public static FieldRef newFieldRef(RunTimeConstantPool runTimeConstantPool, ConstantInfoFieldref info) {
        FieldRef ref = new FieldRef();
        ref.runTimeConstantPool = runTimeConstantPool;
        ref.copyMemberRefInfo(info);
        return ref;
    }

    public  void copyMemberRefInfo(ConstantInfoFieldref refInfo){
        this.className = refInfo.getClassName();
        String[] split = refInfo.getNameAndType().split(":");
        this.name = split[0];
        this.descriptor = split[1];
    }

    public String name(){
        return this.name;
    }

    public String descriptor(){
        return this.descriptor;
    }

    public JField resolvedField() {
        if (null == field) {
            try {
                this.resolveFieldRef();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return this.field;
    }

    private void resolveFieldRef() throws NoSuchFieldException {
        JClass d = this.runTimeConstantPool.getClazz();
        JClass c = this.resolvedClass();

        JField field = this.lookupField(c, this.name, this.descriptor);
        if (null == field){
            throw new NoSuchFieldException();
        }

        if (!field.isAccessibleTo(d)){
            throw new IllegalAccessError();
        }

        this.field = field;
    }

    private JField lookupField(JClass c, String name, String descriptor) {
        for (JField field : c.JFields) {
            if (field.name.equals(name) && field.descriptor.equals(descriptor)) {
                return field;
            }
        }

        for (JClass iface : c.interfaces) {
            JField field = lookupField(iface, name, descriptor);
            if (null != field) return field;
        }

        if (c.superJClass != null) {
            return lookupField(c.superJClass, name, descriptor);
        }

        return null;
    }

}
